from config import *
from player import *
from itertools import izip, count
from dynamicsquares import *
import time, copy, sys

player_dict = {WHITE:'White', BLACK:'Black'}


class Move(object):
    def __init__(self,x,y,color=None):
        self.x = x
        self.y = y
        self.color = color

    def __repr__(self):
        return 'Move(%d,%d,%d)' % (self.x, self.y, self.color)

    #@staticmethod
    def pretty(move):
        return '%s moves to (%s,%s)' % (player_dict[move.color], move.x, move.y)


class Board(object):
    def __init__(self, rownum=8, colnum=8, board=None):
        if board is None:
            self.board = [[EMPTY for _ in range(rownum)] for _ in range(colnum)]
        else:
            self.board = board

    def __eq__(self,other):
        return self.board == other.board

    def flatten(self):
        l = []
        for row in self.board:
            for col in row:
                l.append(col)
        return l
    
    def piece_count(self,player):
        return self.flatten().count(player)

    def in_bounds(self, move):
        # Hardcoded for 8x8
        return 0 <= move.x <= 7 and 0 <= move.y <= 7

        #return 0 <= move.x <= len(self.board[0])-1 and 0<= move.y <= len(self.board)-1

    def valid_move(self, move):
         return self.in_bounds(move) and self.get(move) == EMPTY

    def get(self, move):
        if not self.in_bounds(move):
            return None
        return self.board[move.x][move.y]
    
    def flip(self,move):
        self.board[move.x][move.y] *= -1
    
    def put(self, move):
        self.board[move.x][move.y] = move.color

    def create_move(self,x,y,color=None):
        return Move(x,y,color)

    def __repr__(self):
        out = ''
        for row in self.board:
            out += str(row) + '\n'
        return out

class OthelloBoard(Board):
    def __init__(self, board=None):
        Board.__init__(self,rownum=8,colnum=8,board=board)
        self.player = 1
        

    def copy(self):
        #newboard = OthelloBoard(board = self.board[:])
        #newboard.board = self.board[:]
        #return newboard
        return copy.deepcopy(self)
        #return OthelloBoard(board = self.board[:])

    def setup_board(self):
        init = [ (3, 3, WHITE), (3, 4, BLACK), (4, 3,BLACK), (4, 4,WHITE)]
        [ self.put(Move(x,y,color)) for x,y,color in init ]
        
    def legal_move(self, move):
        return self.valid_move(move) and self.would_flip(move)
        
    def get_flips(self,move):
        if move is None:
            return []
        x, y, color = move.x, move.y, move.color
        target_color = color * -1
        oldx, oldy = x,y
        flips = []
        for cx,cy in move_dirs:
            x = oldx + cx
            y = oldy + cy
            coords = []
            num = 0
            m = Move(x,y,color)
            while self.in_bounds(m) and self.get(m)==target_color:
                coords.append(m)
                x += cx
                y += cy
                m = Move(x,y,color)
            if self.get(m) == color:
                for coord in coords:
                    flips.append(coord)
        return flips

    def would_flip(self, move):
        return self.get_flips(move)

    def num_flips(self,move):
        return len(self.get_flips(move))

    def make_flips(self,move):
        flips = self.get_flips(move)
        for move in flips:
            self.flip(move)

    def legal_moves(self, color=None):
        if color is None:
            color = self.player
        moves = []
        rng = range(8)
        for x in rng:
            for y in rng:
                m = Move(x,y,color)
                if self.legal_move(m):
                    moves.append(m)  
        #moves = [ Move(x,y,color) for x in rng for y in rng  ]
        if not moves:
            return [None]
        return moves

    def obj_to_coords(self, objs):
        li = []
        for obj in objs:
            if obj is not None:
                li.append((obj.x,obj.y,obj.color))
        return li

    def legal_moves_sorted(self,color=None):
        # Sort legal moves using weights from config.py
        if color is None:
            color = self.player
        poss = []
        for x in range(8):
            for y in range(8):
                poss.append((x,y))
        z = zip(weights, poss)
        z.sort()
        moves = [ item[1] for item in z ]
        legal = []
        for move in moves:
            x,y = move
            if self.legal_move(Move(x,y,color)):
                legal.append(Move(x,y,color))
        if not legal:
            return None
        return legal

    def game_over(self):
        a = self.legal_moves(WHITE)
        b = self.legal_moves(BLACK)
        return ((not a) and (not b)) or ((a==[None]) and (b==[None]))


    def make_move(self,move):
        # A move of None means pass
        if move is None:
            self.player = self.player * -1
            return
        # If no color passed, default to current player
        if move.color is None:
            move.color = self.player
        if not self.legal_move(move):
            raise Exception, 'Illegal Move'
        self.put(move)
        self.make_flips(move)
        self.player = self.player * -1

    def score(self):
        return self.piece_count(WHITE) - self.piece_count(BLACK)

    def diff(self):
        return abs(self.score())

    def has_moves(self,color=None):
        return self.legal_moves(color) != [None]
        
        

class Game(object):
    player_dict = {WHITE:'White',BLACK:'Black'}
    def __init__(self, player1,player2,echo=False):
        self.players = {}
        self.players[WHITE]= player1#(WHITE,p1strat)
        self.players[BLACK]= player2#(BLACK,p2strat)
        self.board        = OthelloBoard()
        self.board.setup_board()
        self.echo = echo

    def game_over(self):
        return self.board.game_over()
        
    def play_game(self):
        while not self.game_over():
            for player in (WHITE, BLACK):
                if(self.board.legal_moves(player)):
                    before = time.time()
                    move = self.players[player].get_move(self.board.copy())
                    after = time.time()
                    print '%s took %f seconds.' % (self.player_dict[player],after-before)
                    if(self.echo):
                        print move
                        print self.board
                    self.board.make_move(move)
                

def play_game_log(data):
    'Plays a newline-seperated game log with moves in the form "Move(x,y,color)" '
    moves = data.split('\n')
    game = Game(Player,Player)
    for move in moves:
        if not move.startswith('Move(') and len(move)==11:
            raise Exception, 'Invalid move syntax'
        m = eval(move)
        game.board.make_move


if __name__=='__main__':
    d = 0
    try:
        import psyco
        psyco.full()
    except ImportError:
        print 'Psyco could not be loaded'
    #import profile
    #game = Game(RandomPlayer(WHITE,None),RandomPlayer(BLACK,None))
    game = Game(Minimax(WHITE,dynamic_squares,2), Minimax(BLACK,dynamic_squares,3), echo=True)
    #profile.run('game.play_game()','profile.txt')
    game.play_game()
    print game.board.score()
